package com.gitee.feizns.dynamic.convert;

import com.gitee.feizns.dynamic.Ex;

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Function;

/**
 * <b>数字工具类</b>
 * @author feizns
 * @since 2019/5/19 0019
 */
public abstract class ParseToNumberUtils {

    /**
     * 从{@code java.lang.Number}类型转换为对应具体数字类型的转换函数
     */
    private static final Map<Class<?>, Function<Number, Number>> NUMBER_CLASS_MAPPING_NUMBER_FUNCTION = new HashMap<>();

    static {
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(byte.class, Number::byteValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(short.class, Number::shortValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(int.class, Number::intValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(long.class, Number::longValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(float.class, Number::floatValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(double.class, Number::doubleValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(Byte.class, Number::byteValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(Short.class, Number::shortValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(Integer.class, Number::intValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(Long.class, Number::longValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(Float.class, Number::floatValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(Double.class, Number::doubleValue);
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(BigDecimal.class, number -> BigDecimal.valueOf(number.doubleValue()));
        NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.put(BigInteger.class, number -> BigInteger.valueOf(number.longValue()));
    }

    /**
     * <b>将指定类型转换对应的数字类型</b>
     * @param original 源数据
     * @param targetType 目标类型Class
     * @return 返回数字类型的源对象
     * @param <T> 具体的数字类型
     * @throws NullPointerException 传入参数targetType为null的情况
     */
    @SuppressWarnings("unchecked")
    public static <T extends Number> T to(Object original, Class<T> targetType) {
        if ( original != null ) {
            if ( targetType.isAssignableFrom(original.getClass()) )
                return (T) original;
            else {
                Function<Number, Number> convertFun = NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.get(targetType);
                if ( convertFun != null ) {
                    return original instanceof Number
                            ? (T) convertFun.apply((Number) original)
                            : (T) to(original, convertFun);
                }
            }
        }
        return null;
    }

    /**
     * <b>将original转换为指定的数字类型</b>
     * <p>将original先转换String，再将其转换为对应的数字</p>
     * <pre>{@code
     *  public static void main(String[] args) {
     *     System.out.println(to("1", Number::longValue));     //1
     *     System.out.println(to("1.1", Number::doubleValue)); //1.1
     *     System.out.println(to(new BigDecimal(2), Number::doubleValue)); //1.1
     *  }
     * }</pre>
     * @param original 源数据
     * @param fun 转换函数
     * @return 返回转换函数转换后的值
     * @param <T> 转换后的类型
     * @throws NullPointerException 参数fun为null的情况
     */
    public static <T extends Number> T to(Object original, Function<Number, T> fun) {
        return Ex.tryCatch(() -> original != null ? fun.apply(Double.valueOf(original.toString())) : null)
                .ignoreException(NumberFormatException.class)
                .orElse(null);
    }

    /**
     * <b>是否支持转换</b>
     * @param targetType 转换的目标类型
     * @return 支持转换返回true，不支持返回false
     */
    public static boolean isSupport(Class<?> targetType) {
        return NUMBER_CLASS_MAPPING_NUMBER_FUNCTION.containsKey(targetType);
    }

}
